﻿namespace EasyToolkit.BindingTemplates.Definitions
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using EasyToolkit.BindingTemplates.Definitions.Binding;
    using EasyToolkit.Tools.Reflection;

    public class TemplateDefinition : ITemplateDefinition
    {
        #region attributes and properties
        public TemplateOption TemplateOption
        { get; protected set; }

        public string Name
        { get { return TemplateOption.Name; } }

        public string Description
        { get { return !string.IsNullOrEmpty(TemplateOption.Description) ? TemplateOption.Description : Name; } }

        public Orientation Orientation
        { get { return TemplateOption.Orientation; } }

        public string BindingSource
        { get; private set; }

        public IBindingDefinition MainBindingDefinition
        { get; private set; }

        public List<ILinkedTemplateDefinition> LinkedTemplates
        { get; private set; }

        public bool HasLinkedTemplates
        { get; private set; }

        public List<IBindingDefinition> BindingDefinitions
        { get; private set; }

        public List<IDefinitionPart> BindingParts
        { get; private set; }

        public BindingType BindingType
        { get; protected set; }

        public bool IsSortableAndFilterable
        { get { return TemplateOption.SortAndFilter; } }

        public ITemplateDefinition Header
        { get; protected set; }

        public ITemplateDefinition Footer
        { get; protected set; }
        #endregion

        #region .ctors
        protected TemplateDefinition(string name, TemplateOption templateOption)
        {
            if(templateOption == null)
                throw new ArgumentException("The template option cannot be null");
            if (string.IsNullOrEmpty(templateOption.Name))
                throw new ArgumentException("The template 'Name' cannot be null or empty");

            TemplateOption = templateOption;
            HasLinkedTemplates = false;
            BindingParts = new List<IDefinitionPart>();
            LinkedTemplates = new List<ILinkedTemplateDefinition>();
            BindingDefinitions = new List<IBindingDefinition>();

            Init();
            BindingTemplateManager.Instance.AddTemplateDefinition(this);
        }

        protected TemplateDefinition(string name, TemplateDefinition parent)
        {
            if (parent == null)
                throw new ArgumentException("The parent template definition cannot be null");

            TemplateOption = parent.TemplateOption;
            HasLinkedTemplates = false;
            BindingParts = new List<IDefinitionPart>();
            LinkedTemplates = new List<ILinkedTemplateDefinition>();
            BindingDefinitions = new List<IBindingDefinition>();

            MainBindingDefinition = parent.MainBindingDefinition;
        }
        #endregion

        #region private methods
        private void Init()
        {
            try
            {
                // Get the main binding type
                ////////////////////////////
                if (! string.IsNullOrEmpty(TemplateOption.BindWith))
                {
                    BindingSource = TemplateOption.BindWith.Trim();
                    string[] bindingElements = BindingSource.Split(',');
                    if (bindingElements.Count() != 2 && bindingElements.Count() != 3)
                        throw new ArgumentException("The 'BindingSource' separator is ',' and it must be composed this way 'Assembly,Type[,Method]'");

                    try
                    {
                        Type type = TypeHelpers.GetType(bindingElements[0], bindingElements[1], bindingElements.Count() == 2 ? null : bindingElements[2]);
                        if (type == null)
                            throw new Exception(string.Format("Cannot retrieve type from 'BindingSource': '{0}'", BindingSource));
                        MainBindingDefinition = BindingDefinitionRoot.CreateInstance(type);
                    }
                    catch (Exception ex)
                    {
                        throw new BindingTemplateException("Cannot resolve main binding source", ex);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new BindingTemplateException("Creation of the template definition failed", ex);
            }
        }
        #endregion

        #region protected methods
        protected void AddLinkedTemplate(LinkedTemplateDefinition linkedTemplateDefinition)
        {
            BindingParts.Add(linkedTemplateDefinition);
            LinkedTemplates.Add(linkedTemplateDefinition);
            HasLinkedTemplates = true;
        }

        protected void AddBindingDefinition(IBindingDefinition bindingDefinition)
        {
            BindingParts.Add(bindingDefinition);
            BindingDefinitions.Add(bindingDefinition);
        }
        #endregion
    }
}
